package butines.kraz.entity;

import java.awt.Graphics2D;

import butines.core.Sprite;
import butines.core.SpriteSheet;
import butines.core.game.EntityMoving;
import butines.core.game.Message;
import butines.core.game.fsm.State;
import butines.core.game.fsm.StateMachine;
import butines.core.math.Vector2f;
import butines.kraz.Kraz;
import butines.kraz.state.UnitAttackState;
import butines.kraz.state.UnitDieState;
import butines.kraz.state.UnitHoldState;
import butines.kraz.state.UnitMoveState;
import butines.kraz.state.UnitStayState;

public class Unit extends EntityMoving {

	private Kraz kraz;
	
	private Unit enemy;
	private Vector2f target;
	
	private SpriteSheet sheetCorpse;
	
	private Sprite sprite;
	
	private int health = 100;
	private int healthMax = 100;
	
	private Weapon weapon;
	
	private Action action = new Action();
	
	private Vector2f oldPosition = new Vector2f(getPosition());
	
	private StateMachine<Unit> stateMachine = new StateMachine<Unit>(this);
	
	private Steering steering = new Steering(this);
	
	private PathPlanning pathPlanning = new PathPlanning(this);
	
	private void drawSprite(Graphics2D g, float xScreen, float yScreen) {
		if (sprite != null) {
			sprite.draw(g, xScreen, yScreen, getForward().theta());
		}
	}
	
	public void draw(Graphics2D g, float offsetX, float offsetY) {
		float xScreen = (getPosition().x - offsetX);
		float yScreen = (getPosition().y - offsetY);
		
		drawSprite(g, xScreen, yScreen);
	}
	
	private void updatePosition(float tpf) {
		oldPosition.set(getPosition());
		
		getSteering().calculate();

		Vector2f steeringForce = getSteering().getSteeringForce();
		
		if (stateMachine.inState(UnitStayState.class)) {
			applyBrakingForce(.7f);
		}
		
		applySteeringForce(steeringForce);
		
	}
	
	private void updateSprite(float tpf) {
		if (sprite != null) {
			sprite.update(tpf);
		}
	}
	
	public void update(float tpf) {
		
		updatePosition(tpf);
		
		updateSprite(tpf);
		
		action.update(tpf);
		
		pathPlanning.update(tpf);
		
		getStateMachine().update();
	}

	@Override
	public boolean handleMessage(Message message) {
		final MessageType type = message.getType();
		
		switch (type) {
			
			case RECEIVED_DAMAGE: {
				
				health -= (Integer)message.getArgs(0);
				
				if (isDied()) {
					die();
				}
				
				return true;
			}
			
		}

		return getStateMachine().handlerMessage(message);
		
	}
	
//	public void hitEnemy() {
//		if (enemy == null || enemy.isDied()) {
//			return;
//		}
//		if (canAttackEnemy()) {
//			weapon.hit(this, enemy);
//		}
//	}
	
//	public void lookToEnemy() {
//		if (enemy == null || enemy.isDied()) {
//			return;
//		}
//		getForward().set(enemy.getPosition());
//		getForward().sub(getPosition());
//		getForward().normalise();
//	}
	
//	public boolean canAttackEnemy() {
//		if (enemy == null || enemy.isDied()) {
//			return false;
//		}
//		float minDistance = enemy.getRadius() + getRadius() + weapon.getRange();		
//		return minDistance >= enemy.distance(this);
//	}

//	public void chaseToEnemy() {
//		changeState(UnitChaseToEnemyState.getInstance());
//	}
	
	public void changeState(State<Unit> newState) {
		getStateMachine().changeState(newState);
	}
	
	public boolean atTarget() {
		return Vector2f.distanceSq(getPosition(), target) < 25;
	}
	
	
	public void die() {
		if (inState(UnitDieState.class)) {
			return;
		}
		this.target = null;
		this.enemy = null;
		changeState(UnitDieState.getInstance());
	}
	
	public void stay() {
		if (isDied()) {
			return;
		}
		this.target = null;
		this.enemy = null;
		changeState(UnitStayState.getInstance());
	}
	
	public void move(Vector2f target) {
		if (isDied()) {
			return;
		}
		this.target = target;
		this.enemy = null;
		changeState(UnitMoveState.getInstance());
	}
	
	public void attack(Unit newEnemy) {
		if (isDied() || this == newEnemy || enemy == newEnemy || newEnemy.isDied()) {
			return;
		}
		this.enemy = newEnemy;
		changeState(UnitAttackState.getInstance());
	}

	public void hold() {
		if (isDied()) {
			return;
		}
		this.target = null;
		this.enemy = null;
		changeState(UnitHoldState.getInstance());
	}

	
	public boolean isDied() {
		return health <= 0;
	}
	
	public Sprite getSprite() {
		return sprite;
	}
	
	public void setSprite(SpriteSheet sheetUnit) {
		this.sprite = new Sprite(sheetUnit);
	}
	
	public SpriteSheet getSheetUnit() {
		return sprite == null ? null : sprite.getSheet();
	}

	public SpriteSheet getSheetCorpse() {
		return sheetCorpse;
	}

	public void setSheetCorpse(SpriteSheet sheetCorpse) {
		this.sheetCorpse = sheetCorpse;
	}

	public StateMachine<Unit> getStateMachine() {
		return stateMachine;
	}

	public Action getAction(){
		return action;
	}

	public Vector2f getTarget() {
		return target;
	}

	public void setTarget(Vector2f target) {
		this.target = target;
	}

	public Unit getEnemy() {
		return enemy;
	}

	public void setEnemy(Unit enemy) {
		this.enemy = enemy;
	}

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) {
		this.health = health;
	}

	public int getHealthMax() {
		return healthMax;
	}

	public void setHealthMax(int healthMax) {
		this.healthMax = healthMax;
	}

	public Kraz getKraz() {
		return kraz;
	}

	public void setKraz(Kraz kraz) {
		this.kraz = kraz;
	}

	public Vector2f getOldPosition() {
		return oldPosition;
	}

	public PathPlanning getPathPlanning() {
		return pathPlanning;
	}

	public boolean inState(Class<?> cls) {
		return getStateMachine().inState(cls);
	}

	public Weapon getWeapon() {
		return weapon;
	}

	public void setWeapon(Weapon weapon) {
		this.weapon = weapon;
	}
	
	public Steering getSteering() {
		return steering;
	}

}
